﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.ServiceModel.Web;
using System.ServiceModel.Channels;
using System.Web.Security;
using System.Security.Principal;
using System.IdentityModel.Policy;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.IdentityModel.Claims;
using System.Net;
using System.Reflection;
using Acaweb.Componentes.Contracts;
using Acaweb.Model.Entities;
using Acaweb.Common;

namespace Acaweb.Services.Security.Interceptors
{
    public class BasicAuthenticationInterceptor : RequestInterceptor
    {
        const string Namespace = "Acaweb.Servicios.Contracts.I{0}Service";
        string URL;
        MembershipProvider provider;
        string realm;

        public BasicAuthenticationInterceptor(MembershipProvider provider, string realm)
            : base(false)
        {
            this.provider = provider;
            this.realm = realm;
        }

        protected string Realm
        {
            get { return realm; }
        }

        protected MembershipProvider Provider
        {
            get { return provider; }
        }

        public override void ProcessRequest(ref RequestContext requestContext)
        {
            URL = requestContext.RequestMessage.Headers.To.LocalPath;
            HttpRequestMessageProperty request = (HttpRequestMessageProperty)requestContext.RequestMessage.Properties[HttpRequestMessageProperty.Name];

            string Token = ExtractCredentials(requestContext.RequestMessage);

            if (Token == null) Token = "";

            Sessions Sesion = AuthenticateToken(Token);

            if (Sesion != null && ValidateApplication(request))
                if (HasPermission(Sesion.SessionItems))
                {
                    InitializeSecurityContext(requestContext.RequestMessage, Sesion.SessionItems);
                    return;
                }
           
            Message reply = Message.CreateMessage(MessageVersion.None, null);
            HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.Unauthorized };

            responseProperty.Headers.Add("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", Realm));
            
            reply.Properties[HttpResponseMessageProperty.Name] = responseProperty;
            requestContext.Reply(reply);

            requestContext = null;
        }

        private bool AuthenticateUser(string username, string password)
        {
            if (Provider.ValidateUser(username, password)) { return true; }
            else { return false; }
        }

        public Sessions AuthenticateToken(string Token)
        {
            ISessionsService _Session = AppCtx.Resolve<ISessionsService>();
            Sessions Session = _Session.GetById(Token);

            if (Session != null)
                if (Session.Expires > DateTime.Now)
                    return Session;

            return null;
        }

        private string ExtractCredentials(Message requestMessage)
        {
            HttpRequestMessageProperty request = (HttpRequestMessageProperty)requestMessage.Properties[HttpRequestMessageProperty.Name];
            return request.Headers[HttpRequestHeader.Authorization];
        }

        private string[] DivideURL()
        {
            int First = this.URL.IndexOf("/");
            int Last = this.URL.LastIndexOf("/");

            List<string> URL = new List<string>();
            URL.Add(this.URL.Substring(First + 1, Last - 1));
            URL.Add(this.URL.Substring(Last + 1));

            return URL.ToArray();
        }

        private bool HasPermission(string username)
        {
            Assembly Services = Assembly.Load(AcawebResources.Acaweb_Servicios);//.LoadFile(@"D:\www\UsbctgApi\Acaweb.Services\Bin\Acaweb.Servicios.dll");

            object obj = Services.GetTypes();
            
            string[] SubsURL = DivideURL();

            string Class = SubsURL[0];
            string Method = SubsURL[1];
            
            Type Tipo = Services.GetType(String.Format(Namespace, Class));
            MethodInfo RequestMethod = Tipo.GetMethod(Method);

            object[] Atributtes = RequestMethod.GetCustomAttributes(Services.GetType("Acaweb.Servicios.Utility.AmbitoRealizacion"), true);

            if (Atributtes.Count() > 0)
            {
                Type Ambito = Atributtes[0].GetType();
                PropertyInfo AmbitoProperty = Ambito.GetProperty("Ambito");

                string[] Roles = (string[])AmbitoProperty.GetValue(Atributtes[0], null);

                if (Roles.Count() > 0)
                {
                    string[] CurrentRoles = new Acaweb.Services.Security.Providers.ADRoleProvider().GetRolesForUser(username);
                    bool IsAuthorize = false;

                    foreach (string Rol in Roles)
                    {
                        if (CurrentRoles.Contains(Rol))
                            IsAuthorize = true;
                    }

                    return IsAuthorize;
                }
            }

            return true;
        }

        private bool ValidateApplication(HttpRequestMessageProperty request)
        {
            IApplicationsService ApplicationComponent = AppCtx.Resolve<IApplicationsService>();
            string Ip = request.Headers[HttpRequestHeader.Host];
            string ApplicationId = request.Headers["ApplicationId"];

            return ApplicationComponent.Validate(ApplicationId, Ip);
        }

        private void InitializeSecurityContext(Message request, string username)
        {
            GenericPrincipal principal = new GenericPrincipal(new GenericIdentity(username), new Acaweb.Services.Security.Providers.ADRoleProvider().GetRolesForUser(username));
            
            List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>();
            policies.Add(new PrincipalAuthorizationPolicy(principal));
            ServiceSecurityContext securityContext = new ServiceSecurityContext(policies.AsReadOnly());
            
            if (request.Properties.Security != null)
            {
                request.Properties.Security.ServiceSecurityContext = securityContext;
            }
            else
            {
                request.Properties.Security = new SecurityMessageProperty() { ServiceSecurityContext = securityContext };
            }
        }

        class PrincipalAuthorizationPolicy : IAuthorizationPolicy
        {
            string id = Guid.NewGuid().ToString();
            IPrincipal user;

            public PrincipalAuthorizationPolicy(IPrincipal user)
            {
                this.user = user;
            }

            public ClaimSet Issuer
            {
                get { return ClaimSet.System; }
            }

            public string Id
            {
                get { return this.id; }
            }

            public bool Evaluate(EvaluationContext evaluationContext, ref object state)
            {
                evaluationContext.AddClaimSet(this, new DefaultClaimSet(Claim.CreateNameClaim(user.Identity.Name)));
                evaluationContext.Properties["Identities"] = new List<IIdentity>(new IIdentity[] { user.Identity });
                evaluationContext.Properties["Principal"] = user;
                return true;
            }
        }
    }
}